package com.dji.sample.component.websocket.config;

import com.alibaba.fastjson2.JSONObject;
import com.dji.sample.common.model.CacheModel;
import com.dji.sample.common.model.CustomClaim;
import com.dji.sample.common.model.ResponseResult;
import com.dji.sample.common.model.StoreCache;
import com.dji.sample.component.redis.RedisConst;
import com.dji.sample.component.redis.RedisOpsUtils;
import com.dji.sample.component.websocket.model.BizCodeEnum;
import com.dji.sample.component.websocket.model.CustomWebSocketMessage;
import com.dji.sample.component.websocket.model.PtzMoveText;
import com.dji.sample.component.websocket.model.SocketMsgText;
import com.dji.sample.component.websocket.service.IWebSocketManageService;
import com.dji.sample.component.websocket.service.impl.WebSocketManageServiceImpl;
import com.dji.sample.manage.model.dto.DeviceDTO;
import com.dji.sample.manage.model.enums.UserTypeEnum;
import com.fasterxml.jackson.databind.ObjectMapper;
import jdk.security.jarsigner.JarSigner;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.pqc.crypto.newhope.NHSecretKeyProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.WebSocketHandlerDecorator;

import java.io.IOException;
import java.security.Principal;
import java.util.*;

/**
 * @author sean.zhou
 * @version 0.1
 * @date 2021/11/16
 */
@Slf4j
public class WebSocketDefaultHandler extends WebSocketHandlerDecorator {

    private IWebSocketManageService webSocketManageService;

    private RedisOpsUtils redisOps;

    private ObjectMapper mapper;

    WebSocketDefaultHandler(WebSocketHandler delegate, IWebSocketManageService webSocketManageService, RedisOpsUtils redisOpsUtils, ObjectMapper mapper) {
        super(delegate);
        this.webSocketManageService = webSocketManageService;
        this.redisOps = redisOpsUtils;
        this.mapper=mapper;
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        Principal principal = session.getPrincipal();
        if (StringUtils.hasText(principal.getName())) {
            webSocketManageService.put(principal.getName(), new ConcurrentWebSocketSession(session));
            log.debug("{} is connected. ID: {}. WebSocketSession[current count: {}]",
                    principal.getName(), session.getId(), webSocketManageService.getConnectedCount());
            return;
        }
        session.close();
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        Principal principal = session.getPrincipal();
        if (StringUtils.hasText(principal.getName())) {
            webSocketManageService.remove(principal.getName(), session.getId());
            log.debug("{} is disconnected. ID: {}. WebSocketSession[current count: {}]",
                    principal.getName(), session.getId(), webSocketManageService.getConnectedCount());
        }

    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        SocketMsgText<?> msgText;
        try {
            msgText = JSONObject.parseObject((String) message.getPayload(), SocketMsgText.class);
        } catch (Exception e) {
            session.sendMessage(new TextMessage("参数格式不正确"));
            log.debug("参数格式不正确{}", message.getPayload());
            return;
        }
        if (!StringUtils.hasText(msgText.getScene()) || !StringUtils.hasText(msgText.getDeviceSn()) || msgText.getData() == null) {
            log.info("received message: {}", message.getPayload());
            return;
        }
        switch (msgText.getScene()) {
            case "PTZ":
                sendPtzMoveMsg(msgText, session);
                break;
            default:
                log.info("未知场景消息{}", message.getPayload());
        }
    }

    public void sendPtzMoveMsg(SocketMsgText<?> socketMsgText, WebSocketSession session) throws IOException {
        JSONObject jsonObject = (JSONObject) socketMsgText.getData();
        PtzMoveText ptzMoveText = JSONObject.parseObject(jsonObject.toJSONString(), PtzMoveText.class);
        ConcurrentWebSocketSession socketSession = getCacheSession(socketMsgText.getDeviceSn(), session);
        if (socketSession == null) {
            session.sendMessage(new TextMessage("未找到通话用户"));
            return;
        }
        if(!socketSession.isOpen()){
            session.sendMessage(new TextMessage("未找到通话用户"));
            CacheModel.get("webSocketDefaultHandler").remove(socketMsgText.getDeviceSn());
            return;
        }
        CustomWebSocketMessage<PtzMoveText> msg = CustomWebSocketMessage.<PtzMoveText>builder()
                .timestamp(System.currentTimeMillis())
                .bizCode("PTZ_MOVE")
                .data(ptzMoveText)
                .build();
        socketSession.sendMessage(new TextMessage(mapper.writeValueAsBytes(msg)));
    }


    public ConcurrentWebSocketSession getCacheSession(String deviceSn, WebSocketSession concurrentWebSocketSession) throws IOException {
        //将session 存入 map方便下次查找 过期时间加入时间轮
        StoreCache storeCache = CacheModel.get("webSocketDefaultHandler").name("addCache");
        if (storeCache.containsKey(deviceSn)) {
            return (ConcurrentWebSocketSession) storeCache.get(deviceSn);
        }

        String keys = RedisConst.DEVICE_ONLINE_PREFIX + deviceSn;
        Object o = redisOps.get(keys);
        if (Objects.isNull(o)) {
            concurrentWebSocketSession.sendMessage(new TextMessage("当前设备未在线"));
            return null;
        }
        DeviceDTO device = (DeviceDTO) o;
        String userTypeKey = RedisConst.WEBSOCKET_PREFIX + UserTypeEnum.find(2).getDesc();
        LinkedHashMap<Object, Object> linkedHashMap = (LinkedHashMap<Object, Object>) redisOps.redisTemplate.opsForHash().entries(userTypeKey);
        ListIterator<Map.Entry<Object, Object>> i = new ArrayList<>(linkedHashMap.entrySet()).listIterator(linkedHashMap.size());
        while (i.hasPrevious()) {
            Map.Entry<Object, Object> entry = i.previous();
            if (entry.getValue().equals(device.getUserId())) {
                ConcurrentWebSocketSession socketSession = WebSocketManageServiceImpl.SESSIONS.get(entry.getKey());
                if (!socketSession.isOpen()) {
                    socketSession.close();
                    concurrentWebSocketSession.sendMessage(new TextMessage("用户socket已关闭,请重新登录"));
                    return null;
                }
                storeCache.put(deviceSn, socketSession);
                CacheModel.removeKeyDelay("webSocketDefaultHandler", deviceSn, 30L);
                return socketSession;
            }
        }
        return null;
    }

}